package cn.edu.neu.vmmigeval.cloud.policy.impl;


import cn.edu.neu.vmmigeval.cloud.config.Migration;
import cn.edu.neu.vmmigeval.cloud.entity.Host;
import cn.edu.neu.vmmigeval.cloud.entity.Vm;
import cn.edu.neu.vmmigeval.cloud.policy.MigratedHostPolicy;
import cn.edu.neu.vmmigeval.cloud.policy.VmPlacementPolicy;

import java.util.*;

public class VmPlacementPolicyModifiedFirstFitHeuristic extends VmPlacementPolicy {

	public VmPlacementPolicyModifiedFirstFitHeuristic(List<Host> hostList,
			MigratedHostPolicy migratedHostPolicy) {
		super(hostList, migratedHostPolicy);
	}
	
	public VmPlacementPolicyModifiedFirstFitHeuristic(){
		
	}

	public VmPlacementPolicyModifiedFirstFitHeuristic(MigratedHostPolicy migratedHostPolicy){
		super(migratedHostPolicy);
	}
	public List<Migration> getNewVmPlacement(List<Vm> vmsToMigrate, List<Host> hosts) {
		/*calculateVmSortingFactor(vmsToMigrate);
		sortVmBySortingFactor(vmsToMigrate);
		calculateHostSortingFactor(hosts);
		sortHostBySortingFactor(hosts);*/
		
		List<Host> hsHosts = getExcludedHosts(hosts);
		
		List<Migration>  migrationList = new LinkedList<Migration>();
		for(Vm vm : vmsToMigrate){
			for(Host host : hsHosts){
				if(host.isSuitableForVm(vm)){
					if (isHostOverUtilizedAfterAllocation(host, vm)) {
						continue;
					}
					/*Migration migrate = new Migration();
					vm.setInMigration(true);
					migrate.setSrcHost(vm.getHost());
					migrate.setDestHost(host);
					List<Vm>  vms = new LinkedList<Vm>();
					vms.add(vm);
					migrate.setVms(vms);
					migrationList.add(migrate);
					break;*/
					
					
					Host srcHost = vm.getHost();
					vm.setInMigration(true);
					List<Vm> vms = new ArrayList<Vm>();
					vms.add(vm);
					Migration migration = new Migration();
					migration.setSrcHost(srcHost);
					migration.setDestHost(host);
					migration.setVms(vms);
					migration.setClocks((int)((vm.getCurrentRam()/(host.getTotalBw()-host.getCurrentBw()+10)))+1);
					//System.out.println("clocks is :"+migration.getClocks());
					migration.setEnd(false);
					
					srcHost.setMigration(true);
					host.setMigration(true);
					host.setActive(0);
					migrationList.add(migration);
					break;
					
					
					
				}
			}
		}
		return migrationList;
	}
	
	/**
     * @author zzh
     * @param vmList
     */
	private void sortVmBySortingFactor(List<Vm> vmList) {
        Collections.sort(vmList, new Comparator<Vm>() {
            @Override
            public int compare(Vm a, Vm b) throws ClassCastException {
            	Double aUtilization = Double.valueOf(a.getSortFactor());
                Double bUtilization = Double.valueOf(b.getSortFactor());
                return aUtilization.compareTo(bUtilization);
            }
        });
    }
	
	/**
     * @author zzh
     * @param hosts
     */
	private void sortHostBySortingFactor(List<Host> hosts) {
        Collections.sort(hosts, new Comparator<Host>() {
            @Override
            public int compare(Host a, Host b) throws ClassCastException {
            	Double aUtilization = a.getSortingFactor();
                Double bUtilization = b.getSortingFactor();
                return bUtilization.compareTo(aUtilization);
            }
        });
    }
	
//	/**
//	 * Checks if is host over utilized after allocation.
//	 *
//	 * @param host the host
//	 * @param vm the vm
//	 * @return true, if is host over utilized after allocation
//	 */
	/*protected boolean isHostOverUtilizedAfterAllocation(Host host, Vm vm) {
		boolean isHostOverUtilizedAfterAllocation = true;
		if (host.vmCreate(vm)) {
			isHostOverUtilizedAfterAllocation = this.getMigratedHostPolicy().isHostOverUtilized(host);
			host.vmDestroy(vm);
		}
		return isHostOverUtilizedAfterAllocation;
	}*/
	
	public void calculateVmSortingFactor(List<Vm> vmList){
		double totalCpu = 0;
		double totalRam = 0;
		double totalBw = 0;
		double totalDisk = 0;
		for(Vm vm : vmList){
			totalCpu += vm.getTotalCpu();
			totalRam += vm.getTotalRam();
			totalBw += vm.getCurrentBw();
			totalDisk += vm.getCurrentDisk();
		}
		
		double factor = 0;
		for(Vm vm : vmList){
			factor = vm.getTotalCpu()/totalCpu*4 
					+ vm.getTotalRam()/totalRam
					+ vm.getCurrentBw()/totalBw
					+ vm.getCurrentDisk()/totalDisk;
			vm.setSortFactor(factor);
		}
	}
	
	public void calculateHostSortingFactor(List<Host> hosts){
		for(Host host : hosts){
			double fmax = 1.0;
			double sort = host.getTotalCpu()*4+host.getTotalRam() + host.getTotalBw()+host.getTotalDisk();
			
			host.setSortingFactor(fmax/sort);
		}
	}
}
